//
// xmlint : xml validation tool.
//
// Chris Lovett, September 2001
//
using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Collections.Generic;

namespace xmlint
{
    class XmlValidator
    {
        [STAThread]
        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                PrintUsage();
                return;
            }
            ValidationType validation = ValidationType.Schema;

            List<string> files = new List<string>();
            Catalog cat = new Catalog();
            string catFile = null;
            string inferedSchema = null;
            XmlSchemaInference inf = null;
            XmlSchemaSet iset = new XmlSchemaSet();

            for (int i = 0; i < args.Length; i++)
            {
                String arg = args[i];
                if (arg[0] == '-' || arg[0] == '/')
                {
                    switch (arg[1])
                    {
                        case 'w':
                            validation = ValidationType.None;
                            break;
                        case 's':
                            if (i + 1 < args.Length)
                            {
                                cat.Add(args[++i]);
                            }
                            validation = ValidationType.Schema;
                            break;
                        case 'c':
                             if (i + 1 < args.Length)
                            {
                                 catFile = args[++i];
                                 if (File.Exists(catFile))
                                 {
                                     cat.Load(catFile);
                                 }
                            }
                             break;
                        case 'i':
                             if (i + 1 < args.Length)
                             {
                                 inferedSchema = args[++i];
                                 inf = new XmlSchemaInference();
                             }
                             break;
                        case '?':
                            PrintUsage();
                            return;
                    }
                }
                else
                {
                    string path = Path.GetDirectoryName(args[i]);
                    if (path == "") path = Directory.GetCurrentDirectory();
                    DirectoryInfo di = new DirectoryInfo(path);
                    foreach (FileInfo fi in di.GetFiles(Path.GetFileName(args[i])))
                    {
                        files.Add(fi.FullName);
                    }
                }
            }

            if (!string.IsNullOrEmpty(catFile) && !File.Exists(catFile))
            {
                cat.Save(catFile);
            }

            XmlValidator v = new XmlValidator();

            Uri baseUri = new Uri(Directory.GetCurrentDirectory() + "\\");

            foreach (string filename in files)
            {
                Uri resolved = new Uri(baseUri, filename);
                Uri rel = baseUri.MakeRelativeUri(resolved);
                string rels = rel.GetComponents(UriComponents.SerializationInfoString, UriFormat.SafeUnescaped);

                Console.Write(rels);

                v.Check(resolved, validation, cat);

                if (inf != null)
                {
                    try
                    {
                        XmlReaderSettings rs = new XmlReaderSettings();
                        rs.DtdProcessing = DtdProcessing.Parse;                        
                        rs.MaxCharactersFromEntities = int.MaxValue;
                        rs.MaxCharactersInDocument = int.MaxValue;

                        using (XmlReader r = XmlReader.Create(resolved.AbsoluteUri, rs))
                        {
                            iset = inf.InferSchema(r, iset);
                        }
                    }
                    catch
                    {
                    }

                }
            }

            if (inf != null)
            {
                int index = 0;
                foreach (XmlSchema s in iset.Schemas())
                {                    
                    string fname = inferedSchema + index + ".xsd";
                    XmlWriterSettings ws = new XmlWriterSettings();
                    ws.Indent = true;
                    using (XmlWriter w = XmlWriter.Create(fname, ws))
                    {
                        s.Write(w);
                    }
                }
            }
        }

        bool firstError;

        void Check(Uri uri, ValidationType validation, Catalog cat)
        {
            firstError = true;
            try
            {
                XmlReaderSettings rs = new XmlReaderSettings();
                rs.ValidationType = validation;
                rs.DtdProcessing = DtdProcessing.Parse;
                rs.MaxCharactersFromEntities = int.MaxValue;
                rs.MaxCharactersInDocument = int.MaxValue;
                rs.Schemas = cat.SchemaSet;
                rs.ValidationEventHandler += new ValidationEventHandler(this.ValidationCallback);

                XmlReader reader = XmlReader.Create(uri.AbsoluteUri, rs);                
                while (reader.Read())
                {
                    // it validates while we read !
                }
                if (firstError)
                {
                    Console.WriteLine(", ok");
                }
            }
            catch (XmlSchemaException e)
            {
                ReportParseError(e);
            }
            catch (Exception e)
            {
                if (firstError) Console.WriteLine(", failed");
                firstError = false;
                Console.WriteLine("  ### Exception:" + e.Message);
            }
        }

        public void ReportParseError(XmlSchemaException e)
        {
            if (firstError) Console.WriteLine(", failed");
            firstError = false;
            Console.WriteLine("  Error: " + e.Message);
            if (e.SourceUri != null)
            {
                Console.Write("  File:");
                Console.WriteLine(e.SourceUri);
            }
            if (e.LineNumber > 0)
            {
                Console.Write("  Line:");
                Console.Write(e.LineNumber);
                Console.Write(", Pos:");
                Console.WriteLine(e.LinePosition);
            }
        }

        public void ValidationCallback(object sender, ValidationEventArgs args)
        {
            ReportParseError(args.Exception);
        }

        public static void PrintUsage()
        {
            Console.WriteLine("Usage: xmlint [-w] [-s xsdfile(s)] [-c catalog] <filename>");
            Console.WriteLine("Reports whether the given xml file is valid.");
            Console.WriteLine("Options:");
            Console.WriteLine(" -w makes it just report well-formedness");
            Console.WriteLine(" -s specifies one or more schemas to use to validate, each file is a separate argument ");
            Console.WriteLine(" -c option specifies a schema catalog file.  If the catalog doesn't exist it creates one using the schemas specified on the -s option.");
            Console.WriteLine(" -i option specifies the base file name for output schemas which are infered from the input XML files");
        }

    }
}
